#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <ncurses.h>

struct interface_t
{
    char interface_name[32];
    
    unsigned long long last_rx_bytes;
    unsigned long long last_tx_bytes;

    unsigned long long rx_bytes;   
    unsigned long long rx_packets;
    unsigned long long rx_error;
    unsigned long long rx_drop;
    unsigned long long rx_fifo;
    unsigned long long rx_frame;
    unsigned long long rx_compressed;
    unsigned long long rx_muticast;

    unsigned long long tx_bytes;   
    unsigned long long tx_packets;
    unsigned long long tx_error;
    unsigned long long tx_drop;
    unsigned long long tx_fifo;
    unsigned long long tx_frame;
    unsigned long long tx_compressed;
    unsigned long long tx_muticast;       
};

#define MAX_INTERFACE 32
static int refresh_interval = 2;
static struct interface_t interfaces[MAX_INTERFACE];

void init_interface_stat()
{
    memset(&interfaces, 0, sizeof(interfaces));
}

struct interface_t* find_free_interface()
{
    int i;
    for (i = 0; i < MAX_INTERFACE; ++i)
    {
        if (interfaces[i].interface_name[0] == '\0')
        {
            return &interfaces[i];
        }
    }
    
    return NULL;
}

struct interface_t* find_interface(const char* interface_name)
{
    int i;
        
    for (i = 0; i < MAX_INTERFACE; ++i)
    {
        if (interfaces[i].interface_name[0] != '\0'
            && strcmp(interfaces[i].interface_name, interface_name) == 0)
        {
            return &interfaces[i];
        }
    }
    
    return find_free_interface();
}

void print_number(unsigned long long n)
{    
    if (n < 1024*10)
    {
        printw("%16llu ", n);
    }
    else if (n < 1024*1024*10)
    {
        printw("%16lluk", n/1024);
    }
    else
    {
        printw("%16llum", n/1024/1024);
    }
}

void print_interface_stat()
{
    int i;
    struct interface_t* interface;
    int row; 
    
    move(0, 0);
    printw("interface            send                    recv");
    move(1, 0);
    printw("=================================================");
    
    row = 2;
    
    for (i = 0; i < MAX_INTERFACE; ++i)
    {
        interface = &interfaces[i];
        if (interface->interface_name[0] == '\0')
        {
            continue;
        }
        
        
        move(row, 0);
        printw("%4s", interface->interface_name);
        
        move(row, 8);
        print_number((interface->tx_bytes - interface->last_tx_bytes)/refresh_interval);
        
        move(row, 32);
        print_number((interface->rx_bytes - interface->last_rx_bytes)/refresh_interval);    
        
        row++;       
    }
    
    refresh();
}

void save_to_file(FILE* fp)
{
    struct interface_t* interface;
		int i;
		
	  for (i = 0; i < MAX_INTERFACE; ++i)
    {
        interface = &interfaces[i];
        if (interface->interface_name[0] == '\0')
        {
            continue;
    		}
    		
    		fprintf(fp, "%s %llu %llu ",  interface->interface_name,  
    						(interface->tx_bytes - interface->last_tx_bytes)/refresh_interval, 
    						(interface->rx_bytes - interface->last_rx_bytes)/refresh_interval);
    }
    
    fprintf(fp, "\n");
    fflush(fp);       
}


char* left_trim(char* buf)
{
    char* p = buf;
    while (*p == ' ')
    {
        p++;
    }
    
    return p;    
}

char* get_interface_name(char* buf, char** interface_name)
{
    char* p = strchr(buf, ':');    
    *p = '\0';
    
    *interface_name = left_trim(buf);
        
    return p+1;
}

void save_interface_last_stat()
{
    int i;
    for (i = 0; i < MAX_INTERFACE; i++)
    {
        interfaces[i].last_tx_bytes = interfaces[i].tx_bytes;
        interfaces[i].last_rx_bytes = interfaces[i].rx_bytes;
    }
}

void read_interface_stat(struct interface_t* interface, char* p)
{    
    sscanf(p, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
           &interface->rx_bytes,
           &interface->rx_packets,
           &interface->rx_error,
           &interface->rx_drop,
           &interface->rx_fifo,
           &interface->rx_frame,
           &interface->rx_compressed,
           &interface->rx_muticast,
           
           &interface->tx_bytes,
           &interface->tx_packets,
           &interface->tx_error,
           &interface->tx_drop,
           &interface->tx_fifo,
           &interface->tx_frame,
           &interface->tx_compressed,
           &interface->tx_muticast);    
}

void get_interface_stat()
{
    FILE* fp;
    char* p;
    char* interface_name;
    struct interface_t* interface;
    char buf[512];

    fp = fopen("/proc/net/dev", "r");
    assert(fp != NULL);
    
    fgets(buf, sizeof(buf), fp);
    fgets(buf, sizeof(buf), fp);
    
    while (fgets(buf, sizeof(buf), fp) != NULL)
    {
        p = get_interface_name(buf, &interface_name);
               
        interface = find_interface(interface_name);
        assert(interface != NULL);
                
        strcpy(interface->interface_name, interface_name);

        read_interface_stat(interface, p);          
    }   
    
    fclose(fp);
}


int main()
{   
    initscr();
    
    FILE* fp = fopen("net.stat", "w");
    
    init_interface_stat();
    get_interface_stat();

    while (1)
    {
        save_interface_last_stat();               
        get_interface_stat();      
        print_interface_stat();       
    		if (fp != NULL)
    			save_to_file(fp);
    		
        sleep(refresh_interval);
    }
    
    if (fp != NULL)
    	fclose(fp);
    
    endwin();
    return 0;
} 


